package tenant_db

import (
	"context"
	"errors"
	"git.mycaigou.com/gfyx/common/authorize"
	"git.mycaigou.com/gfyx/common/usermetadata"
	"git.myscrm.cn/golang/stark/v4"
	"google.golang.org/grpc/metadata"
	"gorm.io/gorm"
	"sync"
	"time"
)

type tenantDb struct {
	mysqlConn        stark.MySQLConnIface
	latestActiveTime time.Time //最后一次使用时间
}

type tenantDbPool struct {
	pool sync.Map
}

type TenantDB struct{}

var myDbPool *tenantDbPool

func init() {
	myDbPool = &tenantDbPool{}

	//todo 启动定时轮询，释放长时间未使用的db链接
}

func (c *TenantDB) GetTenantDB(ctx context.Context) *gorm.DB {
	//1.从上下文获取租户code
	tenantCode, err := GetTenantCode(ctx)
	if err != nil {
		stark.Logger.Errorf(ctx, "tenant_db.GetTenantCode err:%s", err.Error())
		return nil
	}
	//2.从内存池中获取连接
	return myDbPool.getTenantDb(ctx, tenantCode)
}

func getMD(ctx context.Context, k string) string {
	md, ok := metadata.FromIncomingContext(ctx)
	if !ok {
		return ""
	}

	v := md.Get(k)
	if len(v) <= 0 {
		return ""
	}

	return v[0]
}

func (c *tenantDbPool) getTenantDb(ctx context.Context, tenantCode string) *gorm.DB {
	cacheDb, ok := c.pool.Load(tenantCode)
	if ok {
		tenantCacheDb := cacheDb.(*tenantDb)
		tenantCacheDb.latestActiveTime = time.Now()
		// fmt.Printf("[%s]租户code:%s,从内存中获取db连接成功，db链接最近一次活跃时间是%s\n", time.Now().String(), tenantCode, tenantCacheDb.latestActiveTime.String())
		return tenantCacheDb.mysqlConn.Get(ctx)
	}
	//内存中没有链接，重新建立链接
	mysqlConn := c.newDbConn(ctx, tenantCode)
	if mysqlConn == nil {
		return nil
	}
	return mysqlConn.Get(ctx)
}

func (c *tenantDbPool) newDbConn(ctx context.Context, tenantCode string) stark.MySQLConnIface {
	var err error
	var tenantMysqlConn stark.MySQLConnIface
	tenantMysqlConn, err = getTenantMysqlConn(ctx, tenantCode)
	if err != nil {
		stark.Logger.Errorf(ctx, "租户code:%s，建立db链接失败:%s", tenantCode, err.Error())
		return nil
	}
	// 租户db句柄对象保存至map中
	c.pool.Store(tenantCode, &tenantDb{
		mysqlConn:        tenantMysqlConn,
		latestActiveTime: time.Now(),
	})
	stark.Logger.Infof(ctx, "租户code:%s，建立db链接成功", tenantCode)
	return tenantMysqlConn
}

func GetTenantBackgroundContext(ctx context.Context, tenantCode string) (context.Context, error) {
	if tenantCode == "" {
		var err error
		tenantCode, err = GetTenantCode(ctx)
		if err != nil {
			stark.Logger.Errorf(ctx, "tenant_db.GetTenantCode err:%s", err.Error())
			return nil, err
		}
	}

	if tenantCode == "" {
		return nil, errors.New("获取租户code失败")
	}

	// 设置租户code和用户信息到上下文
	newCtx := context.WithValue(context.Background(), authorize.TENANT_CODE_KEY, tenantCode)
	curUser := usermetadata.GetMetaUserInfo(ctx)
	newCtx = context.WithValue(newCtx, usermetadata.HEADER_USER_INFO, curUser)

	return newCtx, nil
}

func GetTenantCode(ctx context.Context) (string, error) {
	tenantCode := getMD(ctx, authorize.TENANT_CODE_KEY)
	if tenantCode == "" {
		tenantCode = authorize.GetTenantCode(ctx)
	}
	if tenantCode == "" {
		return "", errors.New("获取租户code失败")
	}
	return tenantCode, nil
}
